/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.snake;

import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class SnakeView extends TileView {
    public Graph graph = new Graph();    
    public void newGraph() {
    	graph = new Graph(this.getWidth(),this.getHeight(),Consts.default_gen_range_scale);
    }
    public int fireState = 0;
    public boolean firebool = false;
    public OnClickListener myButtonListener = new OnClickListener () {
        
   	 public void onClick(View v) {
   		  if (v.getId() == R.id.undo_button)
   			  undo();
   	 }
   	
   };
   
    OnTouchListener myListener= new OnTouchListener() {

        private double pX = 0.0 , pY = 0.0;
        private int selDot = -1;
        @Override  
        public boolean onTouch(View v, MotionEvent event) {
        	
       	switch(event.getAction()) {
      	  		case MotionEvent.ACTION_DOWN: 
      	  				pX = event.getX();
      	  				pY = event.getY();
      	  				selDot = getNearestByXY(pX,pY);
      	  				if (selDot<0) break;
      	  				int x = graph.dotX[selDot];
      	  			    int y = graph.dotY[selDot];
  	  				 	mStatusText.setText(String.valueOf((int) (pX-x))+"**"+String.valueOf((int)(pY-y)));
      	     			break;
      	  		case MotionEvent.ACTION_UP:
      	  				int dX = (int) (event.getX()-pX);
      	  				int dY = (int) (event.getY()-pY);
      	  				if (dX*dX+dY*dY < Consts.max_touch_movedistance_square ) {
      	  					tryReverseByXY(pX,pY);
      	  					checkUndoEnabled();
      	  				//	mStatusText.setText(String.valueOf((int) (dX) +"!"+(int)(dY) ));
      	  				}
      	  				if (mMode!=RUNNING)  {
      	  					initNewGame();
      	  					mMode = RUNNING;
      	  				}
      	  				invalidate();
      	  				selDot = -1;
  	  				  break;
      	  		case MotionEvent.ACTION_MOVE:
      	  			if (selDot>=0) {
      	  			   graph.setXY(selDot,event.getX(),event.getY());
      	  			}
      	  			invalidate();
      	  		 
      	  		}
      
      	  			
        	

  		//  mStatusText.setText(String.valueOf(event.getHistorySize())+"\n"
        //               +String.valueOf(event.getAction())+"\n"
  		//		  +String.valueOf(mTouch)+"\n"+String.valueOf(pX)+","+String.valueOf(pY)
  		//		+ "\n" +String.valueOf(event.getX())+","+String.valueOf(event.getY()));
  		  //mStatusText.setVisibility(View.VISIBLE);	
  		  //return false;
       	return true;
        }
        
        Timer timer;  
        Handler handler = new Handler(){  
            public void handleMessage(Message msg) {  
                if (msg.what<200) {
                  if (msg.what<100) 
                  {
                	  
                	  
                	  mStatusText.setText(String.valueOf(fireState++));
                	  if (fireState >= Consts.max_fire_time) { 
                	      firebool = false;
                	      graph.removeDot(msg.what);
                	      checkUndoEnabled();
                	  }
                	  if (!firebool){
                		  fireState = 0;
                		  timer.cancel();
                	  }
                  }  
                }
                invalidate();
                super.handleMessage(msg);  
                
            }  
              
        };  
        
        
		private void tryReverseByXY(double pX2, double pY2) {
		    if (firebool) return;
			int who = graph.setStateByXY((int) pX2, (int) pY2,2);
			if (who >=0) {
	    		timer = new Timer();
	    		timer.schedule(new TimerTaskClass(who, handler), Consts.wait_time_ms,Consts.wait_time_ms);
	    		firebool = true;
			}
		}
        private int getNearestByXY(double x, double y) {
           return graph.getNearestByXY((int) x, (int) y);
        }
    	
    };
    public void undo() {
       graph.undo();
       invalidate();
       checkUndoEnabled();
    }
    /**
     * Current mode of application: READY to run, RUNNING, or you have already
     * lost. static final ints are used instead of an enum for performance
     * reasons.
     */
    private int mMode = READY;
    public static final int PAUSE = 0;
    public static final int READY = 1;
    public static final int RUNNING = 2;
    public static final int LOSE = 3;
    /**
     * add by sdyy1990;
     * current finger is off
     */
    private int mTouch = 1;
    public static final int ONTOUCH = 0;
    public static final int OFFTOUCH = 1;

    private static final int RED_STAR = 3;
    private static final int YELLOW_STAR = 2;
    private static final int GREEN_STAR = 1;
    
    private static final int FIRE_1 = 4;
    private static final int FIRE_2 = 5;

    /**
     * mScore: used to track the number of apples captured mMoveDelay: number of
     * milliseconds between snake movements. This will decrease as apples are
     * captured.
     */
    private long mScore = 0;
    //private long mMoveDelay = 400;
    /**
     * mLastMove: tracks the absolute time when the snake last moved, and is used
     * to determine if a move should be made based on mMoveDelay.
     */
    //private long mLastMove;
    
    /**
     * mStatusText: text shows to the user in some run states
     */
    private TextView mStatusText;
    private Button  mUndoButton;
    private Button  mNewgameButton;

    public void setTextView(TextView newView) {
        mStatusText = newView;
    }
    public void setUndoButton(Button newButton) {
        mUndoButton = newButton;
    }
    /**
     * Everyone needs a little randomness in their life
     */
    private static final Random RNG = new Random();

    /**
     * Create a simple handler that we can use to cause animation to happen.  We
     * set ourselves as a target and we can use the sleep()
     * function to cause an update/invalidate to occur at a later date.
     */
    //private RefreshHandler mRedrawHandler = new RefreshHandler();

   /* class RefreshHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            SnakeView.this.update();
            SnakeView.this.invalidate();
        }

        public void sleep(long delayMillis) {
        	this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    };

*/
    /**
     * Constructs a SnakeView based on inflation from XML
     * 
     * @param context
     * @param attrs
     */
    public SnakeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initSnakeView();
   }

	public SnakeView(Context context, AttributeSet attrs, int defStyle) {
    	super(context, attrs, defStyle);
    	initSnakeView();
    }
	private int fire_dx;
	private int fire_dy;
    private void initSnakeView() {
        setFocusable(true);
        
        Resources r = this.getContext().getResources();
        
        resetTiles(6);
        loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
  
        loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
        loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
        
        loadTile(FIRE_1,r.getDrawable(R.drawable.fire1));
        loadTile(FIRE_2,r.getDrawable(R.drawable.fire2));
        fire_dx = -mTileArray[FIRE_1].getWidth() / 2;
        fire_dy = -mTileArray[FIRE_1].getHeight();
        
    	this.setOnTouchListener(myListener);
    	//this.set
    }
    

    private void initNewGame() {
    	
        this.setOnTouchListener(myListener);
        newGraph();
        checkUndoEnabled();
    }


    /**
     * Given a ArrayList of coordinates, we need to flatten them into an array of
     * ints before we can stuff them into a map for flattening and storage.
     * 
     * @param cvec : a ArrayList of Coordinate objects
     * @return : a simple array containing the x/y values of the coordinates
     * as [x1,y1,x2,y2,x3,y3...]
     */
    private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
        int count = cvec.size();
        int[] rawArray = new int[count * 2];
        for (int index = 0; index < count; index++) {
            Coordinate c = cvec.get(index);
            rawArray[2 * index] = c.x;
            rawArray[2 * index + 1] = c.y;
        }
        return rawArray;
    }

    /**
     * Save game state so that the user does not lose anything
     * if the game process is killed while we are in the 
     * background.
     * 
     * @return a Bundle with this view's state
     */
    public Bundle saveState() {
        Bundle map = new Bundle();
        map.putIntArray("dotX", graph.dotX);
        map.putIntArray("dotY", graph.dotY);
        map.putIntArray("dotState", graph.dotState);
        
        
/*
        map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
        map.putInt("mDirection", Integer.valueOf(mDirection));
        map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
        map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
        map.putLong("mScore", Long.valueOf(mScore));
        map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));
*/
        return map;
    }

    /**
     * Given a flattened array of ordinate pairs, we reconstitute them into a
     * ArrayList of Coordinate objects
     * 
     * @param rawArray : [x1,y1,x2,y2,...]
     * @return a ArrayList of Coordinates
     */
    private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
        ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();

        int coordCount = rawArray.length;
        for (int index = 0; index < coordCount; index += 2) {
            Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
            coordArrayList.add(c);
        }
        return coordArrayList;
    }

    /**
     * Restore game state if our process is being relaunched
     * 
     * @param icicle a Bundle containing the game state
     */
    public void restoreState(Bundle icicle) {
        setMode(PAUSE);
/*
        mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
        mDirection = icicle.getInt("mDirection");
        mNextDirection = icicle.getInt("mNextDirection");
        mMoveDelay = icicle.getLong("mMoveDelay");
        mScore = icicle.getLong("mScore");
        mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
  */  }

   
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg) {
 return super.onKeyDown(keyCode, msg);
    }
    
    

    public void setMode(int newMode) {
        int oldMode = mMode;
        mMode = newMode;
        this.setOnTouchListener(myListener);
        if (newMode == RUNNING & oldMode != RUNNING) {
            mStatusText.setVisibility(View.INVISIBLE);
            //update();
            return;
        }

        Resources res = getContext().getResources();
        CharSequence str = "";
        if (newMode == PAUSE) {
            str = res.getText(R.string.mode_pause);
        }
        if (newMode == READY) {
            str = res.getText(R.string.mode_ready);
        }
        if (newMode == LOSE) {
            str = res.getString(R.string.mode_lose_prefix) + mScore
                  + res.getString(R.string.mode_lose_suffix);
        }

        mStatusText.setText(str);
        mStatusText.setVisibility(View.VISIBLE);
        mStatusText.setTextColor(Color.RED);
    }

    /**
     * Selects a random location within the garden that is not currently covered
     * by the snake. Currently _could_ go into an infinite loop if the snake
     * currently fills the garden, but we'll leave discovery of this prize to a
     * truly excellent snake-player.
     * 
     */
    

    /**
     * Handles the basic update loop, checking to see if we are in the running
     * state, determining if a move should be made, updating the snake's location.
     */
    
    /**
     * Simple class containing two integer values and a comparison function.
     * There's probably something I should use instead, but this was quick and
     * easy to build.
     * 
     */
    private class Coordinate {
        public int x;
        public int y;

        public Coordinate(int newX, int newY) {
            x = newX;
            y = newY;
        }

        public boolean equals(Coordinate other) {
            if (x == other.x && y == other.y) {
                return true;
            }
            return false;
        }

        @Override
        public String toString() {
            return "Coordinate: [" + x + "," + y + "]";
        }
    }
    

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
     /*
     for (int x = 0; x < mXTileCount; x += 1) {
            for (int y = 0; y < mYTileCount; y += 1) {
                if (mTileGrid[x][y] > 0) {
                    canvas.drawBitmap(mTileArray[mTileGrid[x][y]], 
                    		mXOffset + x * mTileSize,
                    		mYOffset + y * mTileSize,
                    		mPaint);
                }
            }
        }
        
       */
        Paint inqPaint = new Paint();
        Paint ouqPaint = new Paint();
        Paint neqPaint = new Paint();
        inqPaint.setARGB(255, Consts.color_in_clique_R, Consts.color_in_clique_G,Consts.color_in_clique_B );
        ouqPaint.setARGB(255, Consts.color_out_clique_R, Consts.color_out_clique_G,Consts.color_out_clique_B );
        neqPaint.setARGB(255, Consts.color_near_clique_R, Consts.color_near_clique_G,Consts.color_near_clique_B );
        if (!graph.isEmpty) {
        	for (int i = 0 ; i <graph.edgecount; i++) {
            	if (graph.dotState[graph.e1[i]] ==1  && graph.dotState[graph.e2[i]] ==1 )
            		canvas.drawLine(graph.dotX[graph.e1[i]],graph.dotY[graph.e1[i]],
            				        graph.dotX[graph.e2[i]],graph.dotY[graph.e2[i]],ouqPaint);
            }
        	for (int i = 0 ; i < graph.size; i++)
        		for (int j = 0 ; j < graph.size; j++)
        			if (graph.dotState[j] == 1 && graph.dotState[i] ==2 && graph.map[i][j]==1) {
        				int px = (int) (graph.dotX[i] + (graph.dotX[j] - graph.dotX[i])*fireState / Consts.max_fire_time);
        				int py = (int) (graph.dotY[i] + (graph.dotY[j] - graph.dotY[i])*fireState / Consts.max_fire_time);
        				canvas.drawLine(graph.dotX[j],graph.dotY[j],px,py,ouqPaint);
        				canvas.drawBitmap(mTileArray[FIRE_1+fireState % 2], px+fire_dx, py+fire_dy,inqPaint);
        			}
        	
        	for (int i = 0; i < graph.size; i++) 
        		  switch (graph.dotState[i]) {
        		  case 1:
        			  canvas.drawBitmap(mTileArray[YELLOW_STAR], -mXOffset+graph.dotX[i], 
        				-mYOffset+graph.dotY[i],inqPaint);
        			  break;
        		  case 2:  
        			  	canvas.drawBitmap(mTileArray[RED_STAR], -mXOffset+graph.dotX[i], 
  				-mYOffset+graph.dotY[i],inqPaint);
        		  }
        }
        
    }
    public void checkUndoEnabled() {
    	
    	if (graph.removecount==0)
    		mUndoButton.setEnabled(false);
    	else 
    		mUndoButton.setEnabled(true);
    	
    }
	public void setNewGameButton(Button mNewgameButton) {
		this.mNewgameButton = mNewgameButton;
	}

}
